#include "GameManager.h"
#include "PlayerManager.h"
#include "SkillManager.h"
#include "EnemyManager.h"
#include "WaveManager.h"
#include "../data/GameInfo.h"
#include "../ui/BeginInterface.h"

GameManager::GameManager()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
    Mix_Init(MIX_INIT_MP3);
    TTF_Init();
    Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
    SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
    window = SDL_CreateWindow("QxGame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                              GameInfo::Instance().windowWidth,
                              GameInfo::Instance().windowHeight, SDL_WINDOW_SHOWN);
    GAME_RENDERER = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC |
                                                   SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);

    if (!GameInfo::Instance().LoadFromFile()) {
        exit(0);
    }
    BeginInterface::Instance().Init();
    GameInfo::Instance().map.GenerateTileMapTexture();
    GameInfo::Instance().statusBar.position = {10, 15};
    tex = SDL_CreateTexture(GAME_RENDERER, SDL_PIXELFORMAT_ARGB8888,
                            SDL_TEXTUREACCESS_TARGET, GameInfo::Instance().map.w, GameInfo::Instance().map.h);
}

GameManager::~GameManager()
{
    SDL_DestroyRenderer(GAME_RENDERER);
    SDL_DestroyWindow(window);
    TTF_Quit();
    Mix_Quit();
    IMG_Quit();
    SDL_Quit();
}

int GameManager::Run()
{
    unsigned long long lastCounter = SDL_GetPerformanceCounter();
    const unsigned long long counterFreq = SDL_GetPerformanceFrequency();
    GAME_INFO.isGameOver = false;
    GAME_MUSIC.Play(GAME_INFO.musicPool[MusicId::MUSIC_HEISHENHUAPI]);
    while (!isQuit) {
        while (SDL_PollEvent(&GAME_EVENT)) {
            OnInput();
        }
        unsigned long long currentCounter = SDL_GetPerformanceCounter();
        double delta = (double) (currentCounter - lastCounter) / counterFreq;
        lastCounter = currentCounter;
        if (delta * 1000 < 1000.0 / 60) {
            SDL_Delay((Uint32) (1000.0 / 60 - delta * 1000));
        }
        OnUpdate(delta);
        SDL_SetRenderDrawColor(GAME_RENDERER, 0, 0, 0, 255);
        SDL_RenderClear(GAME_RENDERER);
        OnRender();
        SDL_RenderPresent(GAME_RENDERER);
    }
    return 0;
}

void GameManager::Quit()
{
    isQuit = true;
}

void GameManager::InitAssert(bool flag, char *errmsg)
{
    if (flag) return;
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "游戏启动失败", errmsg, window);
    exit(-1);
}

void GameManager::OnInput()
{
    GameInputEvents::OnInput(GAME_EVENT);
    if (GAME_INFO.isGameOver) {
        return;
    }
    if (GAME_PAUSE_MARK) {
        BeginInterface::Instance().OnInput(GAME_EVENT);
        return;
    }
    PlayerManager::Instance().OnInput(GAME_EVENT);
}

void GameManager::OnUpdate(double delta)
{
    if (GAME_INFO.isGameOver) {
        return;
    }
    if (GAME_PAUSE_MARK) {
        BeginInterface::Instance().OnUpdate();
        return;
    }
    GAME_INFO.gameTime = GAME_INFO.gameTime + delta;
    PlayerManager::Instance().OnUpdate(delta);
    SkillManager::Instance().OnUpdate(delta);
    EnemyManager::Instance().OnUpdate(delta);
    WaveManager::Instance().OnUpdate(delta);
    UpdateMapCenter();
}

void GameManager::OnRender()
{
    if (GAME_INFO.isGameOver) {
        SDL_SetRenderTarget(GAME_RENDERER, nullptr);
        SDL_RenderCopy(GAME_RENDERER, GAME_INFO.texturePool[TexId::TEX_END_INTERFACE], nullptr, nullptr);
        return;
    }
    if (GAME_PAUSE_MARK) {
        BeginInterface::Instance().OnRender();
        return;
    }
    Render();
    RenderWindow();
}

void GameManager::Render()
{
    SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
    SDL_SetRenderTarget(GAME_RENDERER, tex);
    GameInfo::Instance().map.OnRender();
    EnemyManager::Instance().OnRender();
    PlayerManager::Instance().OnRender();
    SkillManager::Instance().OnRender();
}

void GameManager::RenderWindow()
{
    SDL_SetRenderTarget(GAME_RENDERER, nullptr);

    SDL_Rect rect = {0, 0, GameInfo::Instance().windowWidth,
                     GameInfo::Instance().windowHeight - GameInfo::Instance().gameWindowHeight};
    SDL_SetRenderDrawColor(GAME_RENDERER, 105, 105, 105, 255);
    SDL_RenderFillRect(GAME_RENDERER, &rect);
    SDL_SetRenderDrawColor(GAME_RENDERER, 0, 0, 0, 255);
    SDL_RenderDrawRect(GAME_RENDERER, &rect);

    auto &map = GameInfo::Instance().map;
    SDL_Rect dst = {GameInfo::Instance().windowWidth - GameInfo::Instance().gameWindowWidth,
                    GameInfo::Instance().windowHeight - GameInfo::Instance().gameWindowHeight,
                    std::min(GameInfo::Instance().gameWindowWidth, map.w),
                    std::min(GameInfo::Instance().gameWindowHeight, map.h)};
    SDL_Rect src = {map.x, map.y,
                    std::min(GameInfo::Instance().gameWindowWidth, map.w),
                    std::min(GameInfo::Instance().gameWindowHeight, map.h)};
    SDL_RenderCopy(GAME_RENDERER, tex, &src, &dst);
    GameInfo::Instance().statusBar.OnRender();
}

void GameManager::UpdateMapCenter()
{
    auto &map = GameInfo::Instance().map;
    auto position = PlayerManager::Instance().GetPosition();
    auto &res = GameInfo::Instance();
    if (map.w <= res.gameWindowWidth) {
        map.x = 0;
    } else {
        map.x = std::max((int) position.x - res.gameWindowWidth / 2, 0);
        map.x = std::min(map.x, map.w - res.gameWindowWidth);
    }

    if (map.h <= res.gameWindowHeight) {
        map.y = 0;
    } else {
        map.y = std::max((int) position.y - res.gameWindowHeight / 2, 0);
        map.y = std::min(map.y, map.h - res.gameWindowHeight);
    }
}

void GameManager::ShowBeginInterFace()
{

}